package com.wlyuan.index.domain.source.valueobj;

import com.wlyuan.core.domain.valueobj.BillId;
import com.wlyuan.core.domain.valueobj.QuotationId;
import com.wlyuan.core.exception.AssertUtils;
import com.wlyuan.core.exception.ValueError;
import com.wlyuan.index.domain.source.Waybill;
import com.wlyuan.tools.utils.CallUtils;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Slf4j
public class WaybillRelation extends Waybill.Relation {
    private final Waybill waybill;
    @Getter
    private final List<Quotation> quotations = new ArrayList<>();
    private final List<Bill> bills = new ArrayList<>();
    private final List<Balance> balances = new ArrayList<>();
    private final List<Waybill.Receipt> receipts = new ArrayList<>();
    private final List<Waybill.Sign> signs = new ArrayList<>();

    public WaybillRelation(Waybill waybill, Waybill.Relation relation) {
        super(relation);
        this.waybill = waybill;
    }

    public void addQuotation(Quotation quotation) {
        AssertUtils.assertEquals(quotation.getTenantId(), this.getTenantId(),
                new ValueError("Add quotation illegal"), quotation, this.getTenantId());
        var filtered = this.quotations.stream()
                .filter(q -> q.getQuotationId().equals(quotation.getQuotationId()))
                .collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(filtered)) {
            logger.warn("Add quotation again: {} {}", filtered, quotation);
            return;
        }
        this.quotations.add(quotation);
    }

    public void addQuotations(List<Quotation> quotations) {
        if (CollectionUtils.isEmpty(quotations)) {
            return;
        }
        quotations.forEach(this::addQuotation);
    }

    public void addBills(List<Bill> bills) {
        this.bills.addAll(bills);
    }

    public void addBalances(List<Balance> balances) {
        this.balances.addAll(balances);
    }

    public void addReceipts(List<Waybill.Receipt> receipts) {
        this.receipts.addAll(receipts);
    }

    public void addSigns(List<Waybill.Sign> signs) {
        this.signs.addAll(signs);
    }

    private Quotation quotation() {
        return quotations.stream()
                .max(Comparator.comparingInt(Quotation::getPriority))
                .orElse(null);
    }

    private Bill bill() {
        return bills.stream()
                .max(Comparator.comparingInt(Bill::getPriority))
                .orElse(null);
    }

    private Balance balance() {
        return this.balances.stream().findAny().orElse(null);
    }

    public QuotationStatus getQuotationStatus() {
        // FIXME: 过关帐期报价状态设置为已关闭
        if (BooleanUtils.isTrue(waybill.getEndBill())) {
            return QuotationStatus.CLOSED;
        }

        var status = CallUtils.call(Quotation::getStatus, this.quotation());
        if (null != status) {
            return status;
        }

        // 运单已关闭，报价设置为已关闭
        if (WaybillStatus.CLOSED == this.getStatus()) {
            return QuotationStatus.CLOSED;
        }

        return null;
    }

    public QuotationId getQuotationId() {
        return CallUtils.call(Quotation::getQuotationId, this.quotation());
    }

    public QuotationNo getQuotationNo() {
        return CallUtils.call(Quotation::getQuotationNo, this.quotation());
    }

    public BillId getBillId() {
        return CallUtils.call(Bill::getBillId, this.bill());
    }

    public BillNo getBillNo() {
        return CallUtils.call(Bill::getBillNo, this.bill());
    }

    public BillStatus getBillStatus() {
        return CallUtils.call(Bill::getStatus, this.bill());
    }

    public String getBillName() {
        return CallUtils.call(Bill::getBillName, this.bill());
    }

    public LocalDateTime getBillAt() {
        return CallUtils.call(Bill::getBillAt, this.bill());
    }

    public boolean isCanBill() {
        if (null == this.getStatus()) {
            return false;
        }

        // 判断运单是否处理可对账状态 运单可对账、运单状态可对账
        var canBill = this.waybill.isCanBill() && this.getStatus().isCanBill();
        return Optional.ofNullable(this.bill())
                .map(bill -> bill.isCanBill() && canBill)
                .orElse(canBill);
    }

    public boolean isCanQuote() {
        if (null == this.getStatus()) {
            return false;
        }

        if (BooleanUtils.isTrue(this.getStopQuote())) {
            // 主动标记不能报价
            return false;
        }

        var billNotAllow = Optional.ofNullable(this.bill())
                .map(bill -> !bill.isCanQuote())
                .orElse(false);
        if (billNotAllow) {
            // 加入对账单不能报价
            return false;
        }

        // 已结账不能报价
        var payNotAllow = Optional.ofNullable(this.balance())
                .map(Balance::getPaymentFee)
                .map(bit -> bit.marked(this.getCharacter()))
                .orElse(false);
        if (payNotAllow) {
            // 已结账不能报价
            return false;
        }

        if (!this.waybill.isCanQuote()) {
            // 运单不能报价
            return false;
        }

        if (!this.getStatus().isCanQuote()) {
            return false;
        }

        // 报价单状态
        return Optional.ofNullable(this.getQuotationStatus())
                .map(QuotationStatus::isCanQuote)
                .orElse(true);
    }

    public BalanceStatus getBalanceStatus() {
        return CallUtils.call(Balance::getStatus, this.balance());
    }

    public String getCustomerName() {
        var customerName = super.getCustomerName();
        if (StringUtils.isEmpty(customerName)) {
            return Optional.ofNullable(waybill.getCustomer())
                    .map(Customer::getName)
                    .filter(StringUtils::isNotEmpty)
                    .orElse(null);
        }
        return customerName;
    }

    public Waybill.Receipt getReceipt() {
        if (CollectionUtils.isEmpty(this.receipts)) {
            return null;
        }

        return this.receipts.get(0);
    }

    public Waybill.Sign getSign() {
        if (CollectionUtils.isEmpty(this.signs)) {
            return null;
        }

        return this.signs.get(0);
    }

    public boolean hasBill() {
        return null != this.bill();
    }

    public boolean hasQuotation() {
        return null != this.quotation();
    }

    public boolean hasBalance() {
        return null != this.balance();
    }
}
